Unity3D.ru • Динамическая загрузка DLL с Mono 您所在的位置:网站首页 unity failed to loaddll Unity3D.ru • Динамическая загрузка DLL с Mono

Unity3D.ru • Динамическая загрузка DLL с Mono

#Unity3D.ru • Динамическая загрузка DLL с Mono| 来源: 网络整理| 查看: 265

Динамическая загрузка DLL с Mono-классами Общие вопросы о Unity3D Ответить Сообщений: 15 • Страница 1 из 1 Динамическая загрузка DLL с Mono-классами

Сообщение Air72 05 янв 2020, 10:54

Доброго времени суток!Занадобилось мне в проект добавлять новый контент не компилируя основную программу. (этакие аддоны делать)Немного изучив "AssetBundle", понял, что C# скрипты в него не загрузишь, да и многие советуют этого не делать, хз, а что такого то?...Тогда я схитрил и создал txt файл своего скрипта и прикрепил к "AssetBundle" вместе с 3d моделью.Подгрузил его не самым хорошим методом, но всё же удачно.Не ругайте сильно за код, я его 15ч из разных источников собирал. Синтаксис:Синтаксис: [ Показать ]Используется csharp IEnumerator Start()         {                 var bundleLoadRequest = AssetBundle.LoadFromFileAsync(Application.dataPath+"/DLC/aLucyDLC");                 yield return bundleLoadRequest;                 var myLoadedAssetBundle = bundleLoadRequest.assetBundle;                 if (myLoadedAssetBundle == null)                 {                         Debug.Log("Failed to load AssetBundle!");                         yield break;                 }                 var assetLoadRequest = myLoadedAssetBundle.LoadAssetAsync("aLucy");                 var assetLoadRequest1 = myLoadedAssetBundle.LoadAssetAsync("SwitchColor");                 yield return assetLoadRequest;                 yield return assetLoadRequest1;                 var txt = assetLoadRequest1.asset as TextAsset;                 string source = txt.ToString();                 Dictionary providerOptions = new Dictionary                 {                         {"CompilerVersion", "v2.0"}                 };                 CSharpCodeProvider provider = new CSharpCodeProvider(providerOptions);                 CompilerParameters compilerParams = new CompilerParameters                 {                         GenerateInMemory = true,                         GenerateExecutable = false                 };                 compilerParams.ReferencedAssemblies.Add("C:\\Program Files\\Unity\\Editor\\Data\\Managed\\UnityEngine.dll");                 CompilerResults results = provider.CompileAssemblyFromSource(compilerParams, source);                 if (results.Errors.Count != 0)                 {                         Debug.Log(results.Errors[0].ErrorText);                         throw new Exception("Mission failed!");                 }                 var assembly = results.CompiledAssembly;                 var type = assembly.GetType("SwitchColor");                 GameObject prefab = assetLoadRequest.asset as GameObject;                 GameObject go = Instantiate(prefab, new Vector3(0,0,0), Quaternion.Euler(new Vector3(0,0,180)));                 go.AddComponent(type);                 myLoadedAssetBundle.Unload(false);         }   Изображение"aLucyDLC" это наш "AssetBundle""aLucy" это префаб модели со скриптом (просто меняющий основной цвет)"SwitchColor" собственно само имя классаВ строке, где я тупо указываю прямой путь к ДЛЛ UnityEngine, нужно для компиляции скрипта из-за того, что мой класс наследник MonoBehaviour.В результате я получаю загруженную 3д модель со скриптом на ней. И при этом всё робит.Только беда в том, что на модели два(!) скрипта. Старый, который был там изначально и новый который добавил.(это вполне нормально)У меня пара-тройка вопросов.1. Мой новый загруженный скрипт "SwitchColor" не видит вся остальная программа. Потому, что он находится в переменной "results". Как мне загрузить скомпилированный код в общую память программы? Что бы можно было использовать класс "SwitchColor" в других скриптах.2. На сколько я понимаю "System.Reflection.Assembly.Load()" мог бы помочь решить эту проблему, только я не соображу из какого места мне загрузить мою сборку?... Скомпилированная находится тут "results.CompiledAssembly", как то в "FileStream" переводить и грузить из потока? Аватара пользователя Air72 UNец   Сообщения: 39Зарегистрирован: 02 май 2014, 10:15 Вернуться к началу Re: Динамическая загрузка DLL с Mono-классами

Сообщение Jarico 05 янв 2020, 13:51

https://docs.unity3d.com/ru/current/Manual/scriptsinassetbundles.html Github: _https://github.com/redheadgektor Discord: Конь! Чаю!#9382 (сижу редко)YouTube: _https://www.youtube.com/channel/UCPQ04Xpbbw2uGc1gsZtO3HQ Telegram: _https:///redheadgektor Аватара пользователя Jarico Адепт   Сообщения: 1069Зарегистрирован: 06 янв 2019, 17:37Откуда: 0xDEAD Skype: none Сайт Вернуться к началу Re: Динамическая загрузка DLL с Mono-классами

Сообщение Air72 05 янв 2020, 14:05

Вот как раз из этого примера я и взял часть кода.Почему не весь? - потому, что у меня не DLL-сборка загружена в "TextAsset txt = bundle.Load("myBinaryAsText", typeof(TextAsset)) as TextAsset;"Как бы я свой скрипт скомпилировал в ДЛЛ в памяти. Но, правильно ли я понимаю, что полученная мной сборка не может быть загружена в общую память приложения? Аватара пользователя Air72 UNец   Сообщения: 39Зарегистрирован: 02 май 2014, 10:15 Вернуться к началу Re: Динамическая загрузка DLL с Mono-классами

Сообщение 1max1 05 янв 2020, 14:28

1. Мой новый загруженный скрипт "SwitchColor" не видит вся остальная программа. Потому, что он находится в переменной "results". Как мне загрузить скомпилированный код в общую память программы? Что бы можно было использовать класс "SwitchColor" в других скриптах.Объяви глобальную статическую переменную своего типа. Но конкретно класс SwitchColor напрямую использовать не получится, то есть вот так:Синтаксис:Синтаксис: [ Показать ]Используется csharpSwitchColor sc = go.GetComponent(); написать ты не сможешь, зато вот так сможешь:Синтаксис:Синтаксис: [ Показать ]Используется csharpobject sc = go.GetComponent(type); object v1 = type.GetField("v1").GetValue(sc);То есть возится с рефлексией придется по любому. Всё-таки лучше не класс подгружать, а сборку, чтобы не возится со всякими провайдерами. Аватара пользователя 1max1 Адепт   Сообщения: 5298Зарегистрирован: 28 июн 2017, 10:51 Вернуться к началу Re: Динамическая загрузка DLL с Mono-классами

Сообщение Air72 05 янв 2020, 14:45

Хм.. В целом понятно, разрабы не стали развивать этот метод, возможно из-за безопасности кода и т.д. и т.п.Я почему с ДЛЛ давно не стал работать, мне не понравились задержки, когда пишешь код, Моно будто подгружает каждый раз классы. А с открытым кодом такого не было.Ну ладно, будем разбираться с DLL. Надеюсь там лучше реализация.Спасибо за ответы:) Аватара пользователя Air72 UNец   Сообщения: 39Зарегистрирован: 02 май 2014, 10:15 Вернуться к началу Re: Динамическая загрузка DLL с Mono-классами

Сообщение 1max1 05 янв 2020, 15:56

Какие еще задержки, ты о чем? Аватара пользователя 1max1 Адепт   Сообщения: 5298Зарегистрирован: 28 июн 2017, 10:51 Вернуться к началу Re: Динамическая загрузка DLL с Mono-классами

Сообщение Air72 05 янв 2020, 16:31

1max1 писал(а):Какие еще задержки, ты о чем?В Моно, когда например MyClass.Param, дак вот когда точку ставишь, он думает, типа как бы загружает инфу о классе, что бы отобразить список доступных методов и т.д.Народ, хэлп!Вот что не так делаю?... Синтаксис:Синтаксис: [ Показать ]Используется csharp                 var bundleLoadRequest = AssetBundle.LoadFromFileAsync(Application.dataPath+"/DLC/dll");                 yield return bundleLoadRequest;                 var myLoadedAssetBundle = bundleLoadRequest.assetBundle;                 if (myLoadedAssetBundle == null)                 {                         Debug.Log("Failed to load AssetBundle!");                         yield break;                 }                 // Первый способ                 //var assetLoadRequest1 = myLoadedAssetBundle.LoadAssetAsync("FirstDLL");                 //yield return assetLoadRequest1;                 //TextAsset txt = assetLoadRequest1.asset as TextAsset;                 // Второй способ                 TextAsset txt = myLoadedAssetBundle.LoadAsset("FirstDLL", typeof(TextAsset)) as TextAsset;                 Debug.Log(txt.name); // name = SwitchColor                 Debug.Log(txt.bytes.Length); // Length = 0                 var assembly = System.Reflection.Assembly.Load(txt.bytes); // Error BadImageFormatException                 var type = assembly.GetType("SwitchColor");   Имя класса есть, даже путь могу поглядеть "Assets\FirstDLL.dll"А почему собственно байт нет?...Упаковываю так Синтаксис:Синтаксис: [ Показать ]Используется csharp #if UNITY_EDITOR using UnityEditor; public class CreateAssetBundles {         [MenuItem ("Assets/Build AssetBundles")]         static void BuildAllAssetBundles ()         {                 BuildPipeline.BuildAssetBundles("AssetBundles", BuildAssetBundleOptions.None, BuildTarget.StandaloneOSXUniversal);         } } #endif   И кстати, если одну ДЛЛ упаковывать, то он вообще ничего не делает. По ходу он только имя классов сохраняет, а чего с кодом длл не так?Создал под 3.5 framework. Юнити сам сказал, что 3.5 только понимает, мол выше нельзя.__________P.S.Дал имя своей длл "FirstDLL.bytes"В итоге " var assembly = System.Reflection.Assembly.Load(txt.bytes);" принял.Length = 4608Debug.Log(assembly.GetExportedTypes()[0]); возвращает "FirstDLL.SwitchColor"А вот "var type = assembly.GetType("SwitchColor");" Не может получить класс, типа его там нет, хах)))) Что не так))_______Додумал.var type = assembly.GetType("FirstDLL.SwitchColor"); если записать так, то работает.Но работает так же как и мой первый вариант в первом посту, блин, только возни больше, что-то не так. Аватара пользователя Air72 UNец   Сообщения: 39Зарегистрирован: 02 май 2014, 10:15 Вернуться к началу Re: Динамическая загрузка DLL с Mono-классами

Сообщение 1max1 05 янв 2020, 17:31

В Моно, когда например MyClass.Param, дак вот когда точку ставишь, он думает, типа как бы загружает инфу о классе, что бы отобразить список доступных методов и т.д.Хм, у меня нет таких проблем... Хотя у меня VS... Аватара пользователя 1max1 Адепт   Сообщения: 5298Зарегистрирован: 28 июн 2017, 10:51 Вернуться к началу [Решено] Динамическая загрузка DLL с Mono-классами

Сообщение Air72 06 янв 2020, 00:29

Решение найдено, хоть и не панацея, но работает. Буду ещё тестировать на более сложных алгоритмах.Выложу решение, может кому пригодится.1. Создание AssetBundle"Первый" проект, там где мы разрабатываем дополнение к нашему "Основному" проекту, сделал так:Есть 3д модель и есть скрипт, который нам необходимо использовать на нём.Компилируем наш скрипт любым доступным средством в DLL.Переносим полученную DLL в проект.Создаём её копию и меняем расширение на "bytes" (честно хз почему так, работает и ладно)Текстовый скрипт можно убрать в резерв из проекта.Создаём префаб нашей 3д модели и переносим нужный класс из реальной DLL на него.Примечание: Если перенести текстовый скрипт, то после загрузки плагина скрипт не присвоится.Далее, выделяем наш префаб и копию DLL-ки с расширением "bytes" в окне "Project".Ставим маркер AssetBundle, например по имени 3д модели. (или как там эта метка называется)Экспортируем наш "AssetBundle" с помощью скрипта "CreateAssetBundles" (что указывал выше)На выходе получим наш плагин. Который после загрузим. Здесь всё.2. Алгоритм загрузки плагина (AssetBundle)Для теста я в сам проект переношу файл плагина. Синтаксис:Синтаксис: [ Показать ]Используется csharp using System; using System.IO; using System.Collections; using System.Collections.Generic; using System.Reflection; using UnityEngine; // Где-то в программе... например в Start или по тригеру в Update // С этого начинается загрузка из плагина this.Invoke("LoadDLLs", 0.1f); // Специально для теста зависания. _nextLoadDLC(); // Если запускать эти методы в родительском потоке, то получим зависание программы.         // Вообще можно было обойтись без этого.         // И в конец метода LoadDLLs() добавить LoadObjects();         // Проверял, работает, но вдруг dll не успеет по каким либо причинам загрузиться? По этому решил сделать так.         private void _nextLoadDLC()         {                 if (_isDLLLoaded)                 {                         LoadObjects();                 }                 else                 {                         this.Invoke("_nextLoadDLC", 0.5f);                 }         }         private bool _isDLLLoaded = false;         void LoadDLLs()         {                 AssetBundle loadedDLC = AssetBundle.LoadFromFileAsync(Application.dataPath+"/DLC/aLucyDLC").assetBundle;                 if (loadedDLC == null)                 {                         Debug.LogError("Не удалось загрузить плагин");                         Debug.Break();                 }                 AssetBundleRequest loadDLL = loadedDLC.LoadAssetAsync("FirstDLL");                 TextAsset DLLAsset = (TextAsset)loadDLL.asset;                 Debug.Log(DLLAsset.bytes.Length);                 // Эти методы оказались эквивалентны.                 Assembly assembly = AppDomain.CurrentDomain.Load(DLLAsset.bytes);                 //Assembly assembly = System.Reflection.Assembly.Load(DLLAsset.bytes);                 // Этот код чисто для отладки, поглядеть, загрузился ли класс.                 Type getClass = assembly.GetType("FirstDLL.SwitchColor");                 Debug.Log(getClass.FullName);                 loadedDLC.Unload(false);                 _isDLLLoaded = true;         }         void LoadObjects()         {                 AssetBundle loadedDLC = AssetBundle.LoadFromFileAsync(Application.dataPath+"/DLC/aLucyDLC").assetBundle;                 if (loadedDLC == null)                 {                         Debug.LogError("Не удалось загрузить плагин");                         Debug.Break();                 }                 AssetBundleRequest loadGO = loadedDLC.LoadAssetAsync("aLucy");                 GameObject prefab = (GameObject)loadGO.asset;                 Debug.Log(prefab.name);                 Instantiate(prefab, new Vector3(0,0,0), Quaternion.Euler(new Vector3(0,0,180)));                 loadedDLC.Unload(false);         }   3. Применение нашего нового класса на уже существующих объектахСкажем так. Это не дело рыться в куче загруженных сборках и искать свою... Извините, просто хотелось по быстрее проверить получится ли его использовать. Синтаксис:Синтаксис: [ Показать ]Используется csharp                         Assembly[] assembly = AppDomain.CurrentDomain.GetAssemblies();                         foreach (Assembly a in assembly)                         {                                 if (a.GetName().Name == "FirstDLL")                                 {                                         var type = a.GetType("FirstDLL.SwitchColor");                                         GO.AddComponent(type);                                 }                         }   Результат выполнения.+ Загруженная из плагина 3д модель появилась без ошибок с работающим скриптом.+ Прикреплённый новый класс корректно начал функционировать на уже существующем объекте.- Как найти свой класс более оптимально, я пока не решил.Да, можно создать глобальный массив со всеми новыми классами и ссылками на них, что бы потом выбирать их от туда, ну как вариант. Аватара пользователя Air72 UNец   Сообщения: 39Зарегистрирован: 02 май 2014, 10:15 Вернуться к началу Re: Динамическая загрузка DLL с Mono-классами

Сообщение Summersay415 06 янв 2020, 07:57

А public значения с помощью такого способа сохраняются? Summersay415 UNец   Сообщения: 19Зарегистрирован: 01 янв 2020, 13:39Откуда: Бийск Вернуться к началу Re: Динамическая загрузка DLL с Mono-классами

Сообщение Air72 06 янв 2020, 10:56

В целом да, сохраняются.Не сохранился почему то собственный класс. Точнее класс остался, только сериализация слетела. Может можно её как то программно включить.Но, из-за того, что сериализация отключилась, потерялись и данные введённые через инспектор.offtopА у меня сотни таких классов... вот попадос=))Вот что тестировалось. Синтаксис:Синтаксис: [ Показать ]Используется csharp                 public enum Air72EnumTest                 {                         None = 0,                         Test1 = 1,                         Test2  =2                 }                 [System.Serializable]                 public class Air72ClassTest                 {                         public string Name;                         public int Index;                         public Air72ClassTest()                         {                         }                 }                 public float FloatTest = 0.83676f;                 public int IntTest = 45;                 public bool BoolTest = true;                 public string StringTest = "ТестКириллицы";                 public Vector3 V3Test = new Vector3(9.31f, 3, 0.183f);                 public Air72EnumTest EnumTest = Air72EnumTest.Test2;                 public Air72ClassTest ClassTest = new Air72ClassTest();   Примеры на картинках.На дефолтных параметрахСкрытый текст: ИзображениеИзображениеИзображениеНа изменённых параметрах.Скрытый текст: ИзображениеИзображениеИзображениеP.S.AudioClip тоже отлично передался и загрузился без проблем. Аватара пользователя Air72 UNец   Сообщения: 39Зарегистрирован: 02 май 2014, 10:15 Вернуться к началу Класс для загрузки AssetBundle

Сообщение Air72 06 янв 2020, 15:24

Организовал отдельный класс, который можно скомпилировать в длл и легко добавлять в проекты.Не стал пока делать делегаты, а так можно и прогресс загрузки показывать.Удобно, если контента много. Синтаксис:Синтаксис: [ Показать ]Используется csharp using System.Collections; using System.Collections.Generic; using UnityEngine; using System.IO; using System; using System.Reflection; /// /// Класс загрузки ресурсов из плагинов /// public class DLCLoader {         private AssetBundle _loadedDLC;         ///         /// Инициализация и загрузка файла плагина         ///         /// Путь к плагину         public DLCLoader(string WayPlugin)         {                 _loadedDLC = AssetBundle.LoadFromFileAsync(WayPlugin).assetBundle;                 if (_loadedDLC == null)                 {                         Debug.LogError("Не удалось загрузить плагин: "+Path.GetFileName(WayPlugin));                         Debug.Break();                 }         }         ///         /// Загружает DLL сборку         ///         /// Имя файла в плагине         public Assembly LoadDLL(string NameAsset)         {                 if (_loadedDLC == null) return null;                 AssetBundleRequest loadDLL = _loadedDLC.LoadAssetAsync(NameAsset);                 Assembly assembly = AppDomain.CurrentDomain.Load(((TextAsset)loadDLL.asset).bytes);                 //Assembly assembly = System.Reflection.Assembly.Load(DLLAsset.bytes);                 return assembly;         }         ///         /// Загружает объект сцены         ///         /// Имя файла в плагине         public GameObject LoadGO(string NameAsset)         {                 if (_loadedDLC == null) return null;                 AssetBundleRequest loadGO = _loadedDLC.LoadAssetAsync(NameAsset);                 return (GameObject)loadGO.asset;         }         ///         /// Загружает аудио файл         ///         /// Имя файла в плагине         public AudioClip LoadAudio(string NameAsset)         {                 if (_loadedDLC == null) return null;                 AssetBundleRequest loadGO = _loadedDLC.LoadAssetAsync(NameAsset);                 return (AudioClip)loadGO.asset;         }         ///         /// Загружает текстуру         ///         /// Имя файла в плагине         public Texture2D LoadTexture(string NameAsset)         {                 if (_loadedDLC == null) return null;                 AssetBundleRequest loadGO = _loadedDLC.LoadAssetAsync(NameAsset);                 return (Texture2D)loadGO.asset;         }         ///         /// Получить указатель на класс         ///         /// Класс         /// Имя DLL         /// Имя класса         public Type GetClass(string NameDLL, string ClassName)         {                 Assembly[] assembly = AppDomain.CurrentDomain.GetAssemblies();                 foreach (Assembly a in assembly)                 {                         if (a.GetName().Name == NameDLL)                         {                                 return a.GetType(NameDLL+"."+ClassName);                         }                 }                 return null;         }         ///         /// Освобождает память от загруженного плагина и уничтожает всё, что было из него добавлено         ///         public void Destroy()         {                 _loadedDLC.Unload(true);         }         ///         /// Освобождает память только от загруженного плагина, добавленные ресурсы сохраняются         ///         public void UnloadDLC()         {                 _loadedDLC.Unload(false);         } } Использование. Синтаксис:Синтаксис: [ Показать ]Используется csharp         public DLCLoader DLCl;         public GameObject GO; // для теста         public AudioSource AS; // для теста         void Start()         {                 DLCl = new DLCLoader(Application.dataPath+"/DLC/aLucyDLC"); // Подключаем наш плагин.                 DLCl.LoadDLL("FirstDLL"); // В первую очередь сразу же грузим классы         }         void Update()         {                 if (Input.GetKeyDown(KeyCode.Alpha1))                 {                         // Загрузка и создание 3д объекта.                         Instantiate(DLCl.LoadGO("aLucy"), new Vector3(0,0,0), Quaternion.Euler(new Vector3(0,0,180)));                 }                 if (Input.GetKeyDown(KeyCode.Alpha2))                 {                         // Получение указателя на наш класс и добавление его на уже существующий объект                         GO.AddComponent(DLCl.GetClass("FirstDLL", "SwitchColor"));                 }                 if (Input.GetKeyDown(KeyCode.Alpha3))                 {                         // Загрузка аудио файла в AudioSource                         AS.clip = DLCl.LoadAudio("Sound_02025");                 }                 if (Input.GetKeyDown(KeyCode.P))                 {                         // Для теста воспроизведения                         AS.Play();                 }   Аватара пользователя Air72 UNец   Сообщения: 39Зарегистрирован: 02 май 2014, 10:15 Вернуться к началу Re: Динамическая загрузка DLL с Mono-классами

Сообщение AngryCat 06 янв 2020, 23:45

Странно, но я не могу включить dll библиотеку в сборку бандла. Пишет ошибку Unrecognized assets cannot be included in AssetBundles: "Assets/Scripts/Arc/Arc.dll". Здесь могла бы быть ваша реклама. Аватара пользователя AngryCat Старожил   Сообщения: 713Зарегистрирован: 20 июл 2018, 22:29 Skype: Дискорд - Флеш#4099 Вернуться к началу Re: Динамическая загрузка DLL с Mono-классами

Сообщение 1max1 06 янв 2020, 23:55

Расширение делай .bytes Аватара пользователя 1max1 Адепт   Сообщения: 5298Зарегистрирован: 28 июн 2017, 10:51 Вернуться к началу Re: Динамическая загрузка DLL с Mono-классами

Сообщение Jarico 07 янв 2020, 13:10

AngryCat писал(а):Странно, но я не могу включить dll библиотеку в сборку бандла. Пишет ошибку Unrecognized assets cannot be included in AssetBundles: "Assets/Scripts/Arc/Arc.dll".Можно расположить DLL на хосте, и скачать с помощью UnityWebRequest и держать в памяти до загрузки бандла Синтаксис:Синтаксис: [ Показать ]Используется csharp byte[] rawAssembly = null;// байты DLL-ки с классами Assembly assembly = Assembly.Load(rawAssembly); //делаем загрузку //ручной вызов метода если нужно Type type = assembly.GetType("Namespace.Class");//получаем типы по пространству имён и классу object obj = Activator.CreateInstance(type); //создаём экземпляр класса MethodInfo method = type.GetMethod("method_1", BindingFlags.Static | BindingFlags.Public);//получаем метод в классе который нужно вызвать (к сожалению только статические) method.Invoke(obj, null); //запускаем метод (null - аргументы)   Важно это делать перед загрузкой бандла в память, если сделать наоборот то двиг не найдёт необходимые классы для бандла и все загруженные ресурсы будут иметь Missing Reference Github: _https://github.com/redheadgektor Discord: Конь! Чаю!#9382 (сижу редко)YouTube: _https://www.youtube.com/channel/UCPQ04Xpbbw2uGc1gsZtO3HQ Telegram: _https:///redheadgektor Аватара пользователя Jarico Адепт   Сообщения: 1069Зарегистрирован: 06 янв 2019, 17:37Откуда: 0xDEAD Skype: none Сайт Вернуться к началу Ответить Сообщений: 15 • Страница 1 из 1

Вернуться в Общие вопросы

Кто сейчас на конференции

Сейчас этот форум просматривают: Google [Bot] и гости: 9




【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有